/*
author:yiliangwu880
you can get more refer from https://gitee.com/yiliangwu880/svr_util.git

brief:执行字符串命令， 游戏作弊码,
使用方法：
	需求修改 log_def.h 定义日志宏
	GamePlayer 修改为自己项目的类
特点：
	1）自动推导注册函数的参数列表解析方法。
		新加作弊功能的方法非常方便，而且可以放在一个代码区域： （免去多个地方声明定义）
		 std::string FuntionName(GamePlayer& player, const string &str, uint32 num){...}
		 REG_PLAYER_DO_CMD(FuntionName);

	2）命令和函数名一样。 比如 "AddMoney 1000" 就执行 AddMoney函数. 

字符串命令用; , . : 空格 分开参数。

缺点：实现原理难理解

sample:
	GameCheat::Ins().DoCmd(player, "AddMoney gold 1000");

	//处理命令"AddMoney gold 1000"
	std::string AddMoney(GamePlayer& player, const string &str, uint32 num)
	{
		... //str == gold, num = 1000
		return "ok";
	}
	REG_PLAYER_DO_CMD(AddMoney);

*/

#pragma once
#include "singleton.h"
#include "typedef.h"
#include "static_reg.h"
#include "cnt_typedef.h"
#include <tuple>
#include <functional>
#include "str_util.h"
//#include "log_def.h" //不同项目，日志宏实现不一样，改吧。

class GamePlayer; //玩家类，不同工程名字不一样，改吧。
namespace su
{
	namespace inner
	{
		template<class T>
		inline bool Unpack(T& t, const std::string& str)
		{
			printf("unpack unknow. type name = %s\n", typeid(T).name());
			static_assert(!(&t), "miss define Unpack function for T");
			return false;
		}
		template<>
		inline bool Unpack<uint32_t>(uint32_t& v, const std::string& str)
		{
			bool isOk = false;
			v = su::StrNum::ToUint32(str, &isOk);
			return isOk;
		}

		template<>
		inline bool Unpack<uint64_t>(uint64_t& v, const std::string& str)
		{
			bool isOk = false;
			v = StrNum::ToUint64(str, &isOk);
			return isOk;
		}
		template<>
		inline bool Unpack<int32_t>(int32_t& v, const std::string& str)
		{
			bool isOk = false;
			v = StrNum::ToInt32(str, &isOk);
			return isOk;
		}
		template<>
		inline bool Unpack<std::string>(std::string& v, const std::string& str)
		{
			v = str;
			return true;
		}

		template<typename Tuple, std::size_t... Is>
		bool TupleUnpackImpl(Tuple& v, const std::string& str, std::index_sequence<Is...>) {

			VecStr vec_str;
			VecStr vec_split;
			vec_split.push_back(";");
			vec_split.push_back(",");
			vec_split.push_back(".");
			vec_split.push_back(" ");
			vec_split.push_back(":");
			StrUtil::split(str, vec_split, vec_str);
			if (vec_str.empty())
			{
				return false;
			}
			if (std::tuple_size<Tuple>::value != vec_str.size() - 1)
			{
				return false;
			}

			bool isSuccess = true;
			auto isOk = [&isSuccess](bool is)
			{
				if (!is)
				{
					isSuccess = false;
				}
			};
			int idx = 1;
			std::initializer_list<int>{(isOk(Unpack(std::get<Is>(v), vec_str[idx++])), 0)...};
			return isSuccess;
		}

		template<class ... Args >
		bool UnpackTuple(std::tuple<Args...>& v, const std::string& str)
		{
			return TupleUnpackImpl(v, str, std::index_sequence_for<Args...>{});
		}

		template<typename ReturnType, typename... Args, typename Tuple, std::size_t... Is>
		ReturnType CallByTupleImplPlayer(ReturnType(*f)(GamePlayer&, Args...), GamePlayer& player, const Tuple& t, std::index_sequence<Is...>)
		{
			return (*f)(player, std::get<Is>(t)...);
		}

		template<typename ReturnType, typename Tuple, typename... Args>
		ReturnType CallByTuplePlayer(ReturnType(*f)(GamePlayer&, Args...), GamePlayer& player, const Tuple& t)
		{
			return CallByTupleImplPlayer(f, player, t, std::index_sequence_for<Args...>{});
			
		}


		template<typename ReturnType, typename... Args, typename Tuple, std::size_t... Is>
		ReturnType CallByTupleImpl(ReturnType(*f)(Args...), const Tuple& t, std::index_sequence<Is...>)
		{
			return (*f)(std::get<Is>(t)...);
		}

		template<typename ReturnType, typename Tuple, typename... Args>
		ReturnType CallByTuple(ReturnType(*f)(Args...), const Tuple& t)
		{
			return CallByTupleImpl(f, t, std::index_sequence_for<Args...>{});
		}
	}

	class GameCheat : public Singleton<GameCheat>
	{
		using DoPlayerCmdFun = std::function< std::string(GamePlayer& player, const std::string& str)>;
		using DoCmdFun = std::function < std::string(const std::string& str)>;
		std::map<std::string, DoPlayerCmdFun> m_str2DoPlayerCmdFun;
		std::map<std::string, DoCmdFun> m_str2DoCmdFun;
	public:
		//执行作弊字符串，返回结果信息。	
		std::string DoCmd(GamePlayer& player, const std::string& str);
		std::string DoCmd(GamePlayer& player, const char *pChar);
		std::string DoCmd(const char* pChar);
		std::string DoCmd(const std::string& str);

		template<typename... Args>
		void RegPlayer(const char* cmd, std::string(*f)(GamePlayer&, Args...))
		{
			auto fun = [f](GamePlayer& player, const std::string& str)->std::string
			{
				std::tuple<std::decay_t<Args>...> t;
				if (!inner::UnpackTuple(t, str))
				{
					return "req para illegal";
				}
				return inner::CallByTuplePlayer(f, player, t);
			};
			m_str2DoPlayerCmdFun[StrUtil::tolower(cmd)] = fun;
		}
		template<typename... Args>
		void Reg(const char* cmd, std::string(*f)(Args...))
		{
			auto fun = [f](const std::string& str)->std::string
			{
				std::tuple<std::decay_t<Args>...> t;
				if (!inner::UnpackTuple(t, str))
				{
					return "req para illegal";
				}
				return inner::CallByTuple(f, t);
			};

			m_str2DoCmdFun[StrUtil::tolower(cmd)] = fun;
		}
	};

#define REG_PLAYER_DO_CMD_EX(cmd, fun)\
	STATIC_RUN(GameCheat::Ins().RegPlayer(#cmd, fun);) 

#define REG_DO_CMD_EX(cmd, fun)\
	STATIC_RUN(GameCheat::Ins().Reg(#cmd, fun);) 

#define REG_PLAYER_DO_CMD(fun)\
	STATIC_RUN(GameCheat::Ins().RegPlayer(#fun, fun);) 

#define REG_DO_CMD(fun)\
	STATIC_RUN(GameCheat::Ins().Reg(#fun, fun);)

}